// Copyright (C) 2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * This provides basic interfaces for apennine XMLRPC services.  These can
 * functions are used to create both client applications and servers.
 * Transport over http is separated from the parsing of XMLRPC messages so
 * that core XMLRPC can be embedded into other protocols as may be desired.
 * @file apennine/xmlrpc.h
 */

#ifndef _APENNINE_XMLRPC_H_
#define _APENNINE_XMLRPC_H_

#ifndef _UCOMMON_LINKED_H_
#include <ucommon/linked.h>
#endif

namespace apennine {

typedef int32_t rpcint_t;

/**
 * XMLRPC parsed message object.  This is used when servers are parsing
 * requests or clients are parsing replies.  Some core methods for
 * xmlrpc message processing are also part of this class.
 */
class __SHARED XMLRPC : protected bufpager
{
private:
    __DELETE_DEFAULTS(XMLRPC);

protected:
    class __LOCAL param : public OrderedObject
    {
    private:
        __DELETE_COPY(param);

    public:
        inline param() : OrderedObject() {}

        const char *name;
        const char *map;
        const char *value;
        const char *type;
        unsigned arg;
    };

    OrderedIndex parsed;
    unsigned count;         // total items parsed
    unsigned argc;          // param count in send
    bool failed;            // response is fault flag
    const char *transaction;        // soap transaction
    const char *method;         // soap method

    param *create(void);
    void add(param *member);

public:
    struct define;

    /**
     * XMLRPC service handlers are grouped together.  This is usually done
     * for all method dispatch handlers that will have a common prefix.  This
     * makes it faster to locate the correct method as well as easier to
     * build requests from groups of method definitions.
     * @author David Sugar <dyfet@gnutelephony.org>
     */
    class __SHARED group : public OrderedObject
    {
    private:
        __DELETE_DEFAULTS(group);

    public:
        /**
         * Construct definition group on an index.
         */
        group(OrderedIndex& index, struct define *list, const char *prefix = "");
        /**
         * Construct definition group for default /rpc2 url.
         */
        group(struct define *list, const char *prefix = "");

        struct define *members;
        const char *methods;
    };

    /**
     * Definition tables holds dispatch methods and introspection data.  The
     * introspection metadata is used by common system.xxxx methods.
     */
    typedef struct define {
        const char *name;
        void (*dispatch)(OrderedIndex& index, XMLRPC *rpc);
        const char *help;
        const char *signature;
    } define_t;

    typedef define_t *methods_t;

    static OrderedIndex common;     // default rpc groups
    static define_t system[];       // default system methods

    /**
     * Construct an xmlrpc message object.  This is used to hold parsed
     * requests and responses.
     * @param paging size.
     */
    XMLRPC(size_t ps);

    /**
     * Return method id of our message.
     * @return method id.
     */
    inline const char *id(void)
        {return method;}

    /**
     * Get size of reply message.  Returns 0 if none created.
     * @return size of reply message.
     */
    inline size_t size(void)
        {return bufpager::used();}

    // reply from methodResponse

    bool is_success(void);
    int error(void);
    const char *fault(void);
    const char *value(const char *type);
    const char *value(unsigned index, const char *type);
    const char *named(const char *name, const char *type);

    operator bool();
    operator double();
    operator rpcint_t();
    operator const char *();

    // call params in methodCall

    const char *value(unsigned param, unsigned offset);
    const char *named(unsigned param, const char *member);
    const char *mapped(const char *map, const char *member);

    inline unsigned params(void)
        {return argc;}

    void success(bool value);
    void success(rpcint_t value);
    void success(const char *value);
    void success(double value);
    void success_time(time_t time);

    void reply(const char *fmt, ...);
    void prep(const char *fmt, ...);
    void success(void);
    void failure(int code, const char *text);

    // xmlrpc message parsing

    bool request(CharacterProtocol& cp);
    bool response(CharacterProtocol& cp);

    static void id(const char *str);
    static void process(OrderedIndex& index, XMLRPC *msg);
};

class __SHARED xmlrpc : public XMLRPC
{
private:
    __DELETE_DEFAULTS(xmlrpc);

public:
    xmlrpc(size_t pager = 4096);

    xmlrpc *create(size_t ps = 4096);
    xmlrpc *request(CharacterProtocol& cp, size_t ps = 4096);
    xmlrpc *response(CharacterProtocol& cp, size_t ps = 4096);
};

typedef xmlrpc *xmlrpc_t;

} // end namespace

#endif
